﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
using System.Security.Permissions;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Outlook.WorkItems.Extension.Manager;
using Outlook.WorkItems.Extension.Managers;

namespace Outlook.WorkItems.Extension.Controls
{
    public class ComboTree : ComboBox
    {

        public void InitCombo()
        {
            this.m_trvNodes.InitTreeView();
        }

        public void InitCombo(string serverUri, string projectName)
        {
            var favorite = FavoritesTeamProjectManager.GetFavoritesProjects().Where(
                f => f.TeamServerUri == serverUri && f.TeamProjectName == projectName).FirstOrDefault();
            if (favorite != null)
                this.m_trvNodes.AddTeamServerNode(favorite, false, false, true, false);
        }

        // Fields
        private TreeViewCancelEventHandler beforeCollapseTreeNode;
        private TreeViewCancelEventHandler beforeExpandTreeNode;
        private const int cMaxDropdownHeight = 300;
        private const int cMinDropdownHeight = 100;
        private const int cTreeViewMargin = 8;
        private ToolStripControlHost m_controlHost;
        private ToolStripDropDown m_dropDown;
        private long m_lastTimeStamp;
        private int m_minValidNodeLevel;
        public ComboTreeView m_trvNodes;

        // Events
        public event TreeViewCancelEventHandler BeforeCollapseTreeNode
        {
            add
            {
                TreeViewCancelEventHandler handler2;
                TreeViewCancelEventHandler beforeCollapseTreeNode = this.beforeCollapseTreeNode;
                do
                {
                    handler2 = beforeCollapseTreeNode;
                    TreeViewCancelEventHandler handler3 = (TreeViewCancelEventHandler)Delegate.Combine(handler2, value);
                    beforeCollapseTreeNode = Interlocked.CompareExchange<TreeViewCancelEventHandler>(ref this.beforeCollapseTreeNode, handler3, handler2);
                }
                while (beforeCollapseTreeNode != handler2);
            }
            remove
            {
                TreeViewCancelEventHandler handler2;
                TreeViewCancelEventHandler beforeCollapseTreeNode = this.beforeCollapseTreeNode;
                do
                {
                    handler2 = beforeCollapseTreeNode;
                    TreeViewCancelEventHandler handler3 = (TreeViewCancelEventHandler)Delegate.Remove(handler2, value);
                    beforeCollapseTreeNode = Interlocked.CompareExchange<TreeViewCancelEventHandler>(ref this.beforeCollapseTreeNode, handler3, handler2);
                }
                while (beforeCollapseTreeNode != handler2);
            }
        }

        public event TreeViewCancelEventHandler BeforeExpandTreeNode
        {
            add
            {
                TreeViewCancelEventHandler handler2;
                TreeViewCancelEventHandler beforeExpandTreeNode = this.beforeExpandTreeNode;
                do
                {
                    handler2 = beforeExpandTreeNode;
                    TreeViewCancelEventHandler handler3 = (TreeViewCancelEventHandler)Delegate.Combine(handler2, value);
                    beforeExpandTreeNode = Interlocked.CompareExchange<TreeViewCancelEventHandler>(ref this.beforeExpandTreeNode, handler3, handler2);
                }
                while (beforeExpandTreeNode != handler2);
            }
            remove
            {
                TreeViewCancelEventHandler handler2;
                TreeViewCancelEventHandler beforeExpandTreeNode = this.beforeExpandTreeNode;
                do
                {
                    handler2 = beforeExpandTreeNode;
                    TreeViewCancelEventHandler handler3 = (TreeViewCancelEventHandler)Delegate.Remove(handler2, value);
                    beforeExpandTreeNode = Interlocked.CompareExchange<TreeViewCancelEventHandler>(ref this.beforeExpandTreeNode, handler3, handler2);
                }
                while (beforeExpandTreeNode != handler2);
            }
        }

        // Methods
        public ComboTree()
        {
            this.DrawMode = DrawMode.OwnerDrawFixed;
            this.TextChanged += new EventHandler(ComboTree_TextChanged);
            this.m_trvNodes = new ComboTreeView(this);
            this.m_trvNodes.AutoSize = true;
            this.m_trvNodes.HotTracking = true;
            this.m_trvNodes.HideSelection = false;
            this.m_trvNodes.BorderStyle = BorderStyle.None;
            this.m_trvNodes.Location = new Point(0, 0);
            this.m_trvNodes.LostFocus += new EventHandler(this.m_trvNodes_LostFocus);
            this.m_trvNodes.BeforeExpand += new TreeViewCancelEventHandler(this.OnBeforeExpand);
            this.m_trvNodes.BeforeCollapse += new TreeViewCancelEventHandler(this.OnBeforeCollapse);
            this.m_trvNodes.NodeMouseClick += new TreeNodeMouseClickEventHandler(this.m_trvNodes_NodeMouseClick);
            this.m_trvNodes.AfterExpand += new TreeViewEventHandler(this.m_trvNodes_AfterExpand);
            this.m_trvNodes.Dock = DockStyle.Top;
            this.m_trvNodes.PathSeparator = "/";

            this.m_controlHost = new ToolStripControlHost(this.m_trvNodes);
            this.m_controlHost.Dock = DockStyle.Fill;
            this.m_controlHost.Margin = new Padding(0);
            this.m_controlHost.Padding = new Padding(0);
            this.m_controlHost.AutoSize = true;
            this.m_dropDown = new ToolStripDropDown();

            this.m_dropDown.AutoClose = false;
            this.m_dropDown.LayoutStyle = ToolStripLayoutStyle.Table;
            this.m_dropDown.Items.Add(this.m_controlHost);
            this.m_dropDown.BackColor = SystemColors.Control;
            this.m_dropDown.Margin = new Padding(0);
        }

        void ComboTree_TextChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(this.Text))
            {
                this.ExpandPath();
                this.Invalidate();
            }
        }

        public void ExpandPath()
        {
            string[] strArray = this.Text.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            TreeNodeCollection nodes = this.Tree.Nodes;
            TreeNode parentTreeNode = null;
            TreeNode node2 = null;
            foreach (string str2 in strArray)
            {
                parentTreeNode = nodes[str2];
                if (parentTreeNode == null)
                {
                    break;
                }
                //this.LoadChildNodes(parentTreeNode);
                nodes = parentTreeNode.Nodes;
                node2 = parentTreeNode;
            }
            if (node2 != null)
            {
                node2.EnsureVisible();
                this.Tree.SelectedNode = node2;
                //this.LoadChildNodes(node2);
                node2.Expand();
            }
            else
            {
                //this.ExpandDefaultRootNode();
            }
        }

        private void ExpandDefaultRootNode()
        {
            //if (this.m_dataProvider != null)
            //{
            //    TreeNode parentTreeNode = null;
            //    TreeNodeCollection nodes = base.Tree.Nodes;
            //    if (nodes.Count == 1)
            //    {
            //        parentTreeNode = nodes[0];
            //    }
            //    else
            //    {
            //        //string defaultRootNodeName = this.m_dataProvider.GetDefaultRootNodeName();
            //        //if ((defaultRootNodeName != null) && (defaultRootNodeName.Length > 0))
            //        //{
            //        //    parentTreeNode = nodes[defaultRootNodeName];
            //        //}
            //    }
            //    if (parentTreeNode != null)
            //    {
            //        //this.LoadChildNodes(parentTreeNode);
            //        parentTreeNode.Expand();
            //        this.Tree.SelectedNode = parentTreeNode;
            //    }
            //}
        }

        private void AsyncHideComboBoxDropDown()
        {
            HideDelegate method = delegate
            {
                string text = this.Text;
                base.DroppedDown = false;
                this.SetSelectedText(text);
            };
            base.BeginInvoke(method);
        }

        protected override AccessibleObject CreateAccessibilityInstance()
        {
            return new ComboTreeAccessibleObject(this);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.m_trvNodes != null)
                {
                    this.m_trvNodes.Dispose();
                }
                if (this.m_dropDown != null)
                {
                    this.m_dropDown.Dispose();
                    this.m_dropDown = null;
                }
            }
            base.Dispose(disposing);
        }

        private void HideTreeDropDown()
        {
            if (this.m_dropDown.Visible)
            {
                this.m_dropDown.Close();
            }
        }

        private int HIWORD(int n)
        {
            return ((n >> 0x10) & 0xffff);
        }

        private void m_trvNodes_AfterExpand(object sender, TreeViewEventArgs e)
        {
            int height = Math.Min(300, this.m_trvNodes.PreferredHeight + 8);
            Rectangle rectangle = base.RectangleToScreen(base.ClientRectangle);
            Rectangle rectangle2 = base.RectangleToScreen(this.m_dropDown.ClientRectangle);
            Rectangle workingArea = Screen.PrimaryScreen.WorkingArea;
            if (rectangle2.Top >= rectangle.Top)
            {
                int num2 = (workingArea.Height - rectangle.Y) - rectangle.Height;
                if ((height < num2) && (this.m_dropDown.Height < height))
                {
                    this.m_dropDown.MinimumSize = new Size(base.Width, height);
                    this.m_dropDown.Height = height;
                }
            }
        }

        private void m_trvNodes_LostFocus(object sender, EventArgs e)
        {
            this.IsDroppedDown = false;
        }

        private void m_trvNodes_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (((e.Node != null && e.Node is QueryNode) && (e.X >= e.Node.Bounds.Left)) && (e.Node.Level >= this.m_minValidNodeLevel))
            {
                this.SetSelection(e.Node);
            }
        }

        protected virtual void OnBeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            if (this.beforeCollapseTreeNode != null)
            {
                this.beforeCollapseTreeNode(sender, e);
            }
        }

        protected virtual void OnBeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            if (this.beforeExpandTreeNode != null)
            {
                this.beforeExpandTreeNode(sender, e);
            }
        }

        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            this.Tree.Font = this.Font;
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if ((base.DropDownStyle == ComboBoxStyle.DropDownList) && !e.Alt)
            {
                switch (e.KeyCode)
                {
                    case Keys.Right:
                    case Keys.Down:
                        this.IsDroppedDown = true;
                        e.Handled = true;
                        break;
                }
            }
            base.OnKeyDown(e);
        }

        public event TreeViewEventHandler NodeSelected;

        protected virtual void OnNodeSelected(TreeNode node)
        {
            if (NodeSelected != null && node is QueryNode)
            {
                NodeSelected(this, new TreeViewEventArgs(node));
            }
        }

        private ToolStripDropDownDirection SetDropdownHeight()
        {
            Rectangle workingArea = Screen.FromControl(this).WorkingArea;
            Point point = base.PointToScreen(Point.Empty);
            int num = (workingArea.Height - point.Y) - base.Height;
            int height = Math.Max(100, Math.Min(300, this.m_trvNodes.PreferredHeight + 8));
            this.m_dropDown.MinimumSize = new Size(base.Width, height);
            this.m_dropDown.MaximumSize = new Size(base.Width, height);
            this.m_trvNodes.Size = new Size(base.Width - 8, height - 8);
            if (this.m_dropDown.Height < num)
            {
                return ToolStripDropDownDirection.BelowRight;
            }
            return ToolStripDropDownDirection.AboveRight;
        }

        protected void SetSelectedText(string text)
        {
            if (base.DropDownStyle == ComboBoxStyle.DropDownList)
            {
                base.Items.Clear();
                base.Items.Add((text != null) ? text : string.Empty);
            }
            this.Text = text;
        }

        protected void SetSelection(TreeNode treeNode)
        {
            SelectionDelegate method = delegate(TreeNode node)
            {
                //try
                //{
                this.IsDroppedDown = false;
                this.SetTextForNode(node);
                this.OnNodeSelected(node);
                //}
                //catch (Exception exception)
                //{
                //    throw;
                //}
            };
            base.BeginInvoke(method, new object[] { treeNode });
        }

        protected void SetTextForNode(TreeNode node)
        {
            this.SetSelectedText((node != null) ? node.FullPath : string.Empty);
        }

        private void ShowTreeDropDown()
        {
            if (!this.m_dropDown.Visible)
            {
                this.OnDropDown(EventArgs.Empty);
                ToolStripDropDownDirection direction = this.SetDropdownHeight();
                Point position = base.PointToScreen(Point.Empty);
                if (direction == ToolStripDropDownDirection.BelowRight)
                {
                    position.Y += base.Height;
                }
                this.m_dropDown.Show(position, direction);
            }
            this.m_trvNodes.Focus();
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m)
        {
            if ((m.Msg == 0x2111) && (this.HIWORD((int)m.WParam) == 7))
            {
                if ((this.m_lastTimeStamp + 0xf4240L) < DateTime.Now.Ticks)
                {
                    this.IsDroppedDown = !this.IsDroppedDown;
                }
            }
            else
            {
                base.WndProc(ref m);
            }
        }

        // Properties
        private bool IsDroppedDown
        {
            get
            {
                return this.m_dropDown.Visible;
            }
            set
            {
                this.m_lastTimeStamp = DateTime.Now.Ticks;
                if (value)
                {
                    this.ShowTreeDropDown();
                }
                else
                {
                    this.HideTreeDropDown();
                }
                this.AsyncHideComboBoxDropDown();
            }
        }

        protected int MinValidNodeLevel
        {
            get
            {
                return this.m_minValidNodeLevel;
            }
            set
            {
                this.m_minValidNodeLevel = value;
            }
        }

        protected TreeView Tree
        {
            get
            {
                return this.m_trvNodes;
            }
        }

        [Description("Nodes collection of internal Treeview"), Browsable(true), Category("Tree")]
        public TreeNodeCollection TreeNodes
        {
            get
            {
                return this.m_trvNodes.Nodes;
            }
        }

        // Nested Types
        private class ComboTreeAccessibleObject : Control.ControlAccessibleObject
        {
            // Fields
            private const int cNumControls = 1;
            private Control m_control;

            // Methods
            public ComboTreeAccessibleObject(Control control)
                : base(control)
            {
                this.m_control = control;
            }

            public override AccessibleObject GetChild(int index)
            {
                if (index == 0)
                {
                    return ((ComboTree)this.m_control).Tree.AccessibilityObject;
                }
                return null;
            }

            public override int GetChildCount()
            {
                return 1;
            }

            // Properties
            public override AccessibleRole Role
            {
                get
                {
                    return AccessibleRole.Grouping;
                }
            }

            public override AccessibleStates State
            {
                get
                {
                    return AccessibleStates.None;
                }
            }
        }

        public class ComboTreeView : TeamExplorerControl
        {
            // Fields
            private ComboTree m_parent;

            // Methods
            public ComboTreeView(ComboTree parent)
            {
                this.m_parent = parent;
                base.SetStyle(ControlStyles.Selectable, false);

                this.ShowLines = false;
                this.HotTracking = true;
                int num = (int)MyNativeMethods.SendMessage(this.Handle, 0x112d, IntPtr.Zero, IntPtr.Zero);
                num |= 4;
                num |= 0x40;
                MyNativeMethods.SendMessage(this.Handle, 0x112c, IntPtr.Zero, (IntPtr)num);
                MyNativeMethods.SetWindowTheme(this.Handle, "explorer", null);
            }

            private int GetTotalVisibleCount(TreeNodeCollection nodes)
            {
                int count = nodes.Count;
                foreach (TreeNode node in nodes)
                {
                    if (node.IsExpanded)
                    {
                        count += this.GetTotalVisibleCount(node.Nodes);
                    }
                }
                return count;
            }

            protected override void OnKeyDown(KeyEventArgs e)
            {
                if (((e.KeyCode == Keys.Return) || (e.KeyCode == Keys.Return)) || (e.KeyCode == Keys.Space))
                {
                    this.SetSelection();
                }
                else if (e.KeyCode == Keys.Tab)
                {
                    this.SetSelection();
                    MyNativeMethods.PostMessage(new HandleRef(this, this.m_parent.Handle), 0x100, (IntPtr)((long)e.KeyCode), (IntPtr)((long)e.KeyData));
                }
                else if ((e.KeyCode == Keys.F4) || (e.KeyCode == Keys.Escape))
                {
                    this.m_parent.IsDroppedDown = false;
                }
                else if (e.Alt && ((e.KeyCode == Keys.Up) || (e.KeyCode == Keys.Down)))
                {
                    this.m_parent.IsDroppedDown = false;
                }
                else
                {
                    base.OnKeyDown(e);
                }
            }

            public override bool PreProcessMessage(ref Message msg)
            {
                if (msg.Msg == 0x100)
                {
                    if (((int)msg.WParam) == 9)
                    {
                        this.SetSelection();
                        MyNativeMethods.PostMessage(new HandleRef(this, this.m_parent.Handle), 0x100, msg.WParam, msg.LParam);
                        return true;
                    }
                    if (((int)msg.WParam) == 0x1b)
                    {
                        this.m_parent.IsDroppedDown = false;
                        return true;
                    }
                }
                return base.PreProcessMessage(ref msg);
            }

            private void SetSelection()
            {
                if (base.SelectedNode.Level >= this.m_parent.MinValidNodeLevel)
                {
                    this.m_parent.SetSelection(base.SelectedNode);
                }
            }

            // Properties
            public int PreferredHeight
            {
                get
                {
                    return ((this.GetTotalVisibleCount(base.Nodes) + 1) * base.ItemHeight);
                }
            }
        }

        private delegate void HideDelegate();

        private delegate void SelectionDelegate(TreeNode node);


        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            base.OnDrawItem(e);
            e.DrawBackground();
            if (e.Index >= 0)
            {
                string text = this.Text;
                //if (!this.m_isRootLoaded)
                //{
                //    this.LoadRootNodes();
                //    this.ExpandPath();
                //}
                bool enabled = base.Enabled;
                if (!enabled)
                {
                    using (Brush brush = new SolidBrush(SystemColors.Control))
                    {
                        e.Graphics.FillRectangle(brush, e.Bounds);
                    }
                }
                int num = -1;
                if (this.Tree.ImageList != null)
                {
                    TreeNode selectedNode = this.Tree.SelectedNode;
                    num = ((selectedNode != null) && (selectedNode.FullPath == this.Text)) ? selectedNode.ImageIndex : -1;
                }
                if (num >= 0)
                {
                    int num2 = (e.Bounds.Height - this.Tree.ImageList.ImageSize.Height) / 2;
                    Rectangle rect = Rectangle.FromLTRB(e.Bounds.Left, e.Bounds.Top + num2, e.Bounds.Left + this.Tree.ImageList.ImageSize.Width, e.Bounds.Top + this.Tree.ImageList.ImageSize.Height);
                    Rectangle bounds = Rectangle.FromLTRB(rect.Right, e.Bounds.Top + 1, e.Bounds.Right, e.Bounds.Bottom);
                    if ((num >= 0) && (num < this.Tree.ImageList.Images.Count))
                    {
                        if (enabled)
                        {
                            e.Graphics.DrawImage(this.Tree.ImageList.Images[num], rect);
                        }
                        else
                        {
                            ControlPaint.DrawImageDisabled(e.Graphics, this.Tree.ImageList.Images[num], rect.Left, rect.Top, SystemColors.Control);
                        }
                    }
                    TextRenderer.DrawText(e.Graphics, text, e.Font, bounds, enabled ? e.ForeColor : SystemColors.GrayText, TextFormatFlags.TextBoxControl);
                }
                else
                {
                    Rectangle rectangle3 = Rectangle.FromLTRB(e.Bounds.Left, e.Bounds.Top + 1, e.Bounds.Right, e.Bounds.Bottom);
                    TextRenderer.DrawText(e.Graphics, text, e.Font, rectangle3, enabled ? e.ForeColor : SystemColors.GrayText, TextFormatFlags.TextBoxControl);
                }
            }
            e.DrawFocusRectangle();

        }
    }

    public class MyNativeMethods
    {
        [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int PostMessage(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32", CharSet = CharSet.Unicode)]
        internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
        [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        public static extern int SetWindowTheme(IntPtr hWnd, string subAppName, string subIdList);


    }
}
